//! Thread support using rt-thread API

use crate::base::*;
use alloc::string::String;
use crate::alloc::boxed::Box;
use core::mem;
use crate::base::rttbase_thread_startup;
use crate::base::rttbase_thread_create;
use crate::base::rttbase_thread_delay;
use crate::base::rttbase_thread_yield;

#[derive(Debug)]
pub struct Thread (*const CVoid);

impl Thread {
    pub fn delay(tick: u32) {
        rttbase_thread_delay(tick);
    }

    pub fn mdelay(tick: i32) {
        rttbase_thread_mdelay(tick);
    }

    pub fn new() -> ThreadInfo {
        ThreadInfo {
            th_name: "uname".into(),
            th_stack_size: 4096,
            th_priority: 10,
            th_ticks: 10,
        }
    }

    pub fn _yield() {
        rttbase_thread_yield();
    }

    pub fn delete_thread(th: Self) {
        rttbase_thread_delete(th.0);
    }

    pub fn delete(&self) {
        rttbase_thread_delete(self.0);
    }

    unsafe fn spawn_inner(name: String,
                  stack_size: u32,
                  priority: u8,
                  ticks: u32,
                  func: Box<dyn FnOnce()>) -> Result<Self, RTTError>
    {
        let func = Box::new(func);
        let param = &*func as *const _ as *mut _;

        extern "C" fn thread_func(param: *mut CVoid) {
            unsafe {
                let run = Box::from_raw(param as *mut Box<dyn FnOnce()>);
                run();
            }
        }

        let ret = rttbase_thread_create(name.as_ref(), thread_func,
                                        param, stack_size,priority,ticks);

        return if ret == 0 as *const CVoid {
            Err(RTTError::OutOfMemory)
        }
        else{
            let s = Self::_startup(ret);
            let ret = match s {
                Ok(_) => {
                    mem::forget(func);
                    Ok(Thread(ret))
                }
                Err(e) => Err(e)
            };
            ret
        }
    }

    fn _startup(th: *const CVoid) -> Result<(), RTTError> {
        let ret = rttbase_thread_startup(th);
        return if ret != 0 {
            Err(RTTError::ThreadStartupErr)
        } else{
            Ok(())
        }
    }

    pub fn spawn<F>(name: String,
                  stack_size: u32,
                  priority: u8,
                  ticks: u32,
                  func: F) -> Thread
        where F: FnOnce() -> () + Send + 'static
    {
        unsafe {
            return Self::spawn_inner(name, stack_size, priority, ticks, Box::new(func))
                .unwrap();
        }
    }
}

unsafe impl Send for Thread {}

pub struct ThreadInfo {
    th_name: String,
    th_stack_size: u32,
    th_priority: u8,
    th_ticks: u32,
}

impl ThreadInfo{
    pub fn name(&mut self, name: &str) -> &mut Self {
        self.th_name = name.into();
        self
    }
    
    pub fn stack_size(&mut self, stack_size: u32) -> &mut Self {
        self.th_stack_size = stack_size;
        self
    }
    
    pub fn priority(&mut self, priority: u8) -> &mut Self {
        self.th_priority = priority;
        self
    }

    pub fn ticks(&mut self, ticks: u32) -> &mut Self {
        self.th_ticks = ticks;
        self
    }

    pub fn start<F>(&self, func: F) -> Result<Thread, RTTError>
        where F: FnOnce() -> (),
              F: Send + 'static
    {
        Ok(Thread::spawn(self.th_name.clone(),
                       self.th_stack_size,
                       self.th_priority,
                       self.th_ticks,
                       func))
    }
}

